from __future__ import absolute_import
from forest.resource import Resource, ValidationError
from os.path import join
from os import sep
from copy import deepcopy

class File(Resource):
    ''' File resources are copied to the target.

        Example::

            resources:
            - $: file
              path: /root/.bash_history
              mode: 0644
              source: files/bash_history

        .. describe:: path

            Required. Destination of file to copy into the target's root.

        .. describe:: source

            Optional. Source file to copy. One and only one of source, template must be specified.

            Relative paths are interpreted relative to the tree file the
            resource is defined.

        .. describe:: template

            Optional. Template file to apply. One and only one of source, template must be specified.

            Relative paths are interpreted relative to the tree file the
            resource is defined.

        .. describe:: mode

            Optional. An octal number representing the desired permissions.
            (default: 0644)
    '''
    __register__ = "file"

    # cached copy of the content
    _content = None

    # cached copy of the md5
    _md5 = None

    def __init__(self, config, value):
        super(File, self).__init__(config, value)

    def  __add__(self, other):
        ''' Boilerplate.
        '''

        f = super(File, self).__add__(other)
        print self.get_source(), '!!!!!!!!!!!!!!!!!!!!adding {0} to {1}'.format(self, other)
        #if self.get_source()[0] == '/':#.startswith('/'):
        #    print '$$$$$$$$$$$$$$$$$$$$$$$overriding'
        f.config = other.config
        if getattr(f, '_content', None) is not None:
            del f._content

        return f

    def get_id(self):
        ''' Boilerplate.
        '''
        return ''.join(['file:', self.value.get('path')])

    def validate(self):
        # TODO: if mode is defined, check that it is valid. try int octal conversion.
        valid_keys = ['path', 'source', 'template', 'type', 'mode']

        for k in self.value.iterkeys():
            if k in valid_keys:
                continue
            raise ValidationError("Undefined file option %s." % k)

        too_many = 'source' in self.value and 'template' in self.value
        # having both or not having either trigger this
        if too_many or ('source' not in self.value and 'template' not in self.value):
            raise ValidationError("Must specify one and only one of source, template .")

        if 'path' not in self.value:
            raise ValidationError("File resource missing destination path.")

        if not self.get_path().startswith('/'):
            raise ValidationError("File destination must be absolute path. Got: %s" % self.get_path())

    def get_content(self, source = None):
        ''' Returns the content of the file resource.

            Note that relative paths are translated relative to the
            configuration file this resource was defined in.
        '''
        if not source:
            source = self.get_source()
        if not self._content:
            print self.get_config().url.path
            self._content, self._md5 =  self.get_config().get_file_and_md5(source)
        return self._content

    def get_url(self, source = None):
        if not source:
            source = self.get_source()
        return self.get_config().translate_path(source).geturl()

    def get_md5(self):
        ''' Returns the md5 checksum of the file.
        '''
        if self._md5 is None:
            # use get_content to preload the md5
            self.get_content()

        return self._md5

    def get_source(self):
        ''' Returns a path where the file contents can be found.
        '''
        return self.value.get('source')

    def get_template(self):
        ''' Returns a path where the file template can be found.
        '''
        return self.value.get('template')

    def get_path(self):
        ''' Returns a path where the file contents should go.
        '''
        return self.value.get('path')

    def get_mode(self):
        ''' Returns the mode this file should be in.
        '''
        return self.value.get('mode', 0644)


# NS: THE FOLLOWING CODE IS CRAP
#def to_num(pattern, aritmf = None, record = None):
#
#    if aritmf is None:
#        aritmf = lambda x, y: x + y
#    if record is None:
#        record = perm_to_num_r
#
#    ret = 0
#    for letter in pattern:
#        try_new = record.get(letter, None)
#        if try_new:
#            ret = aritmf(ret, try_new)
#    return ret
#
#def ref_to_lvl(*args, **kwargs):
#    kwargs.update({'record': ref_to_lvl_r})
#    return to_num(*args, **kwargs)
#
#def perms_to_num(perms):
#    if not isinstance(perms, basestring):
#        return perms
#    else:
#        int_perms = 0
#        perm_to_num_r = {'r': 4, 'w':2, 'x':1}
#        ref_to_lvl_r = {'u': 0100, 'g': 010, 'o': 01, 'a': 0111}
#
#        for perm in perms.split(','):
#            try:
#                level, pattern = perm.split('+')
#                int_perms += ref_to_lvl(level) * to_num(pattern)
#            except ValueError:
#                pass
#
#            try:
#                level, pattern = perm.split('-')
#                int_perms += ref_to_lvl(level) * to_num(pattern, aritmf = lambda x, y: x-y)
#            except ValueError:
#                pass
#        return int_perms
